home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
TPUG - Toronto PET Users Group
/
TPUG Users Group CD
/
TPUG Users Group CD.iso
/
AMIGA
/
AMICUS
/
AMIBEST1.ADF
/
AmigaBasicStuff
/
BasicGadgets
/
Gadgets.DOC
< prev
next >
Wrap
Text File
|
1986-11-06
|
20KB
|
485 lines
Catley,Basic Gadgets,Page 1
As I'm sure you're aware, gadgets are those little boxes
containing a word or two, or some type of symbol, which appear in
windows and invite you to click on them to cause some predetermined
event to occur. Unfortunately, Amiga Basic contains no built-in
functions for drawing or checking gadgets. However, it is a
relatively easy task to draw and check your own gadgets, and that's
what we're going to look at! Not only will we see how to do it, but
we'll also see how to do all this from three subprograms that may be
merged into any program which requires them. You will, in effect,
create your own gadget functions!
A Simple Gadget
The simplest form of gadget is just a box with a word in it. For
example:
CLS:LINE (36,20)-(92,36),1,b:LOCATE 4,6:PRINT "Gadget"
From a purely functional point of view, there is nothing wrong
with this gadget, but you will probably prefer one that is a little
more pleasing to the eye. However, there is an important lesson to be
learned from this simple gadget! Choosing the gadget coordinates!
The first thing to do is to decide on the location of the text
which will appear within the gadget, and then choose the appropriate
coordinates for the box itself. In the example above, the box is
drawn four pixels, or half a character wider and higher than the text
itself. You may, of course, draw your box any distance from the text
you decide, but if it is too big it will look a little odd! The most
important thing to remember is that text is, for the most part, drawn
at a specific row and column location, and the box should surround the
text evenly.
A Prettier Gadget
Okay, let's see what we can do to make this gadget a little nicer
to look at; something we'd like to have in our programs! How about if
we make the box solid and then outline it with a contrasting color?
While we're at it, let's insert an inner outline, and place a "shadow"
on the right and lower sides! Try the following program to see what
it looks like:
LINE (36,20)-(92,36),1,bf
LINE (36,20)-(92,36),3,b
LINE (38,22)-(90,34),3,b
LINE (93,21)-(93,37),2
LINE (93,37)-(37,37),2
COLOR 3:LOCATE 4,6:PRINT "Gadget"
If more than one gadget is present at the same time, we can even
use different colors; how about green for a "Yes" gadget, and red for
a "No" gadget?
Please note that the rest of our discussion will be based on
Catley,Basic Gadgets,Page 2
gadgets of the "prettier type" described above. Feel free to
establish your own design, but it will also become necessary for you
to make the appropriate changes through-out the remainder of our
discussion!
The Components of a Gadget
By now it should be pretty obvious that to draw each gadget
separately, using the code shown above, would be very cumbersome and a
very poor approach. This is the perfect situation in which to use a
subroutine. But we're going one step further, and use a subprogram!
The two big reasons for this are that subprograms are a lot more
independent (very important when they will be used in many programs),
and they can be used in a similar fashion to other functions, i.e.
"name parameters"; and this makes the program easier to read and less
complicated.
Now, what information about each gadget is our subprogram, (let's
name it "DrawGdgts"), going to need? Well, for starters, it will need
the coordinates of the upper left-hand corner and either the length
and height in pixels, or the coordinates of the lower right-hand
corner. We'll use the former, it's easier! Also, we're going to need
the color of the gadget's background, the color of the border and
contents, the color of the "shadow", and finally, the text that is to
be inserted in the gadget. That's eight different pieces of
information!
The most obvious way of handling this is to pass all eight pieces
of information to "DrawGdgts" each time we want to draw a gadget.
Using our earlier example, we'd have:
DrawGdgts 36,20,56,16,1,3,2,"Gadget"
While there is nothing particularly bad about this approach, its
biggest disadvantage is that each gadget must be drawn individually;
we cannot specify a range of gadgets to draw.
This is not terribly important when drawing gadgets (unless you have a
lot of them on the screen at once), but it does take on some
significance when checking which gadget has been selected. Thus, it
becomes important to be able to specify a range of gadgets to be drawn
or checked - which is why we called the subprogram "DrawGdgts" rather
than "DrawGdgt"!
Needless to say, given this requirement, the information about
the gadgets must be stored in an array; and since we have both numeric
and string data, we'll actually need two arrays. This automatically
means the need to DIMension two arrays, and to provide a series of
DATA statements which describe the gadgets.
This might be done as follows:
NumGdgts=4:DIM Gdgts(NumGdgts-1,6),GdgtTxt$(NumGdgts-1)
The following points are of interest:
Catley,Basic Gadgets,Page 3
-A variable (NumGdgts) is used to specify the actual number of
gadgets that will be used. As we add or delete gadgets to our
programs, we only have to change the value of one variable
rather than searching through the entire program for every
reference to the number of gadgets.
-The "Gdgts" array is two-dimensional; one seven position entry
for each gadget.
-Remember that arrays start at entry zero, so they are
dimensioned to the maximum number less one.
The associated DATA statements would look something like:
DATA 36, 20, 56, 16, 1, 3, 2, "Gadget"
In other words, it contains the same eight pieces of information
we've already been discussing! One important point must be made here,
just in case it has not become obvious! Since we will be drawing and
checking our gadgets in ranges, gadgets that will appear on the screen
at the same time, must be grouped together in the DATA statements.
Saving the Gadget's Components
All of this brings us to the first of our three subprograms,
creating the arrays from the DATA statements. Let's call this
subprogram "BldGdgts". Now, what information is "BldGdgts" going to
need to perform its defined function of building the two gadget
arrays? Well, it's going to need to know the total number of gadgets,
and the names of the two arrays. We'll pass all three pieces of
information as parameters; we could use the SHARED statement, but
while we'll remove the need for parameters, we would be forced into
using the same names in every program, and that might not be what we
really want. So, we'll invoke our "BldGdgts" subprogram with one of
two statements types:
CALL BldGdgts (NumGdgts,Gdgts(),GdgtTxt$())
or
BldGdgts NumGdgts,Gdgts(),GdgTxt$
Either form is correct and the choice is strictly a personal one.
Note how the arrays are specified. The () is required for Basic to
know it is an array being passed and not a variable.
Look at Listing #1 and then type it in. This is our "BldGdgts"
subprogram; it is a pretty straight-forward use of the READ statement
to extract values from DATA statements and place them in arrays. Note
that while the specified parameters must be in the same sequence, they
do not need the same names, and that variable names are unique within
a subprogram; i.e. "x" in a subprogram is a different variable from
Catley,Basic Gadgets,Page 4
"x" in a main program.
Since we will eventually be combining the other two subprograms
with "BldGdgts", pick an appropriate name to save it as! How about
"Gadgets"? And remember to save it with the ",A" option; for example:
SAVE "Gadgets",A
This will allow the code to be merged in with other programs at a
later time. N.B. You must use an immediate command to do this, the
Project Save menu item cannot be used, at least not for the first
save.
Drawing the Gadgets
All of the array information is now stored in arrays, and the
time has come to use "DrawGdgts". To do its job, this subprogram
needs to know the starting and ending gadgets to be drawn, and the
name of the arrays containing the gadget data. Thus, we might use
CALL DrawGdgts (2,5,Gdgts(),GdgtTxt$())
to draw gadgets three through six from the arrays; (remember, the
first entry in an array is entry zero).
Now, "DrawGdgts" itself will set up a loop, and for each gadget
to be drawn, it will:
-extract the necessary data from the array, (not strictly
necessary, but it allows Basic to make the array calculation
only once, and it also allows shorter, more usable names to be
applied to the values)
-draw the gadget in much the same fashion as we did earlier
-print the text in the gadget after calculating the starting row
and column
Listing #2 shows the "DrawGdgts" subprogram. It also shows
something new! The inner outline, text and shadow are only drawn if
the "shadow" color is greater than -1 (or is a valid palette number)!
This minor extension allows us to handle another type of gadget very
easily.
Up to this point we've been discussing "Yes/No" type gadgets
which will "flash" when clicked on, and which are used simply to
indicate the user's choices. Another type of gadget is used for data
entry purposes. For example, in a requester, a user clicks in a box
to indicate some data will be entered. The box reverses its outline
and interior colors and all is ready for some data to be entered.
Since this type of gadget will not use a "shadow", we can use
this field to indicate the type of gadget by using an illegal palette
number. So, if we desire this form of gadget, we set the
"shadow" color to -1 and everything will be taken care of
automatically!
Back to "DrawGdgts"; if a -1 is detected as the shadow color,
Catley,Basic Gadgets,Page 5
then only the box and its outline are drawn. The inner outline and
the text are ignored.
Now, LOAD "Gadgets" (or whatever you called it) and extend it by
adding Listing #2 to the end, and then save it again. The ",A" should
not be necessary this time, and the Project Menu save item may also
be used. Once saved with the ",A", all future saves (or replaces)
will automatically use the ASCII format.
Which Gadget was Clicked?
Our third subprogram, "GetGdgt" is also our most complicated! So
before looking at how it works, let's review how
the mouse might be used to check for a simple gadget being clicked.
Remember, MOUSE(0) returns a zero value until the left button is
pressed when MOUSE(1) returns the x coordinate of the mouse pointer,
and MOUSE(2) returns the y coordinate. The following sample program
draws a simple gadget, and will not quit
until you click in the gadget. Try it!
LINE (36,28)-(92,42),1,B 'Draw Gadget
LOCATE 5,7:PRINT"Quit" 'Insert Text
Ok=0 'Set Flag=0
WHILE Ok=0 'Loop till flag not zero
WHILE MOUSE(0)=0:WEND 'Wait for click
x=MOUSE(1):y=MOUSE(2) 'Pick up x & y coordinates
IF x>36 AND x<92 THEN 'Check x coordinates
IF y>28 AND Y<42 THEN 'Check y coordinates
Ok=1 'Set flag=1 if in gadget
END IF
END IF
WEND
LOCATE 1,1:PRINT"You did it!"
END
The above is pretty straight forward, and once you understand it,
"GetGdgt" will be that much easier to follow.
To do its job, "GetGdgt" (Listing #3) needs to know the range of
gadgets to be checked, the names of the gadget arrays, and to have a
means of letting the caller know which gadget was clicked in. The
latter may be handled with a variable that is set to the relative
number of the gadget clicked in, within the given range. This being
the case,
CALL GetGdgt (2,5,Gdgts(),GdgtTxt$(),gdgt)
may be used to determine which gadget, if any, has been clicked. In
this example, "gdgt" will return 0-4 depending on whether the click
was outside all the gadgets, or was in one of gadgets 3, 4, 5, or 6
respectively. If "gdgt" comes back as zero, we'll almost certainly
want to loop back and issue the call again. The following is an
example of how "GetGdgt" might actually be used in many situations:
Catley,Basic Gadgets,Page 6
gdgt=0
WHILE gdgt=0
CALL GetGdgt (2,5,Gdgts(),GdgtTxt$(),gdgt)
WEND
ON gdgt GOTO Rtn3,Rtn4,Rtn5,Rtn6
The obvious question right now is "Why can't GetGdgt be written
to wait for a click in one of the specified gadgets?" The answer, for
now, is "Flexibility". We'll discuss a more practical reason later!
Beside the five parameters, "GetGdgt" also shares three variables
with the main program. All are conveniences for the main program; two
pass back the actual x and y coordinates selected, and the third is
simply an indicator that a valid gadget was, indeed, selected. The
first two are useful when the program needs to know where the pointer
was in the gadget when it was clicked, while the third can be useful
if the program is waiting on more events than just a gadget being
selected.
Anyway, once "GetGdgt" is called, it waits for a click, sets
variables and picks up the x and y coordinates of the pointer. It
then loops through the specified range of gadgets to see if any of
them were clicked. If one was, the gadget is drawn in its opposite
colors (starting the "flash"), the relative number of the gadget
within the specified range is calculated and set, and the loop is
terminated. At this point, the subprogram waits for the left button
to be released, when it redraws the gadget in its usual colors, (if
the gadget has been selected and the shadow color is greater than -1
and thereby ending the flash). Before returning to the main program,
the cursor is relocated to its original position. (The need for this
last function is not that obvious, but I found it the hard way when
trying to combine user input and gadget selection!)
The listing of "GetGdgt" may look a little complicated, but it
really isn't. It's just the example we looked at earlier with a few
bells and whistles thrown in!
It's now time to add "GetGdgt" to "Gadgets". Load "Gadgets" and
extend it with Listing #3 and save the entire thing. You could have
saved each of the three subprograms separately if you wished, but
since the chance of one being used without the other is very slim, it
makes more sense to combine them into one program unit.
Let's Try Them Out
After all that effort we'd better make sure they work correctly!
Listing #4 shows a sample program which exercises all three
subprograms. Enter it as shown, and then click in the Basic Output
Window and enter the immediate command: MERGE "Gadgets" (or whatever
you called the subprograms).
The three subprograms should be appended to Listing #4 in your
Edit Window. If you receive a "Bad File Mode" error message you
probably forgot to use the ",A" option when you originally saved
"BldGdgts". No problem! Just save Listing #4, load "Gadgets" again
Catley,Basic Gadgets,Page 7
and then resave it with the ",A" option; (use the immediate command:
SAVE "Gadgets",A), reload Listing #4 and then issue the MERGE again.
Once you have them combined, save the entire program as, say,
"GdgtDemo"; (no need to use the ",A" option now).
If you look at Listing #4, it wont be too long before you notice
two things that are different from what we have discussed so far.
First, there is a gadget with a shadow color of -2, and we are using a
completely different method of waiting for the mouse click!
The former is easy, it just gives us a third type of gadget
without any need to modify the gadget subprograms. This gadget will
not change at all when it is clicked in. A useful option when
selecting an item from a list to obtain further information in a
separate window.
The second is a little more involved. Rather than use the method
of checking the mouse described above, we set up a mouse event
trapping routine, (ON MOUSE GOSUB GetMouse); turn it on, (MOUSE ON);
and then, when it's time to wait for the click, we go to SLEEP until
the user clicks the button. At this point, control automatically
passes to the "GetMouse" routine which simply invokes "GetGdgt". The
RETURN results in control being passed back to the statement
immediately following the one that was being executed when the "mouse
event" occurred. In our case, this is the WEND following the SLEEP.
Now, if "gdgt" is still zero, the program goes right back to SLEEP; if
it is non-zero, it responds appropriately. In other words, we are
waiting for the click, via the SLEEP statement, outside of the
"GetGdgt" routine.
Why is this an important option? Well, assume you have a program
which puts up a "Yes/No" requester, and then goes to "GetGdgt" and
dutifully waits at the WHILE MOUSE(0) statement. Meanwhile, the user
scratches his/her head and decides to use the help menu that you have
thoughtfully provided, and at the bottom of the help window, lo and
behold, is an "OK" gadget. The result of this is that when the help
routine tries to go to "GetGdgt", the program crashes with an error
message informing you that a subprogram cannot be used by two users at
the same time! It is, therefore, important to wait outside of the
"GetGdgt" subprogram! SLEEP is used because it does not require any
CPU cycles (unlike WHILE MOUSE(0)=0:WEND) and this is very important
in a multitasking machine such as the Amiga. SLEEP is also useful
when you're waiting for one of several events to occur. Just set up
the event traps and go to SLEEP; there is no need to establish a loop
which is constantly checking on all the events you are waiting on!
All this is not to say that polling (WHILE MOUSE(0)=0:WEND)
should never be used. Event traps are just an alternate (but probably
better) method, which involves a little more programming, but which
offers a number of advantages.
We got side tracked, back to our "GdgtDemo" program! Once you've
entered Listing #4, MERGEd in the "Gadgets" subprograms, and saved it;
RUN it. The screen will show a number of gadgets examples in the
lower portion, and will have two "real" gadgets at the top, "More" and
"Quit". If you click in any of the example gadgets, you'll receive a
Catley,Basic Gadgets,Page 8
message telling you which one you clicked. If you select "More",
you'll receive a second screen of example gadgets, with "Repeat" and
"Quit" gadgets at the top. As you might expect, "Repeat" returns you
to the first set of examples, and "Quit" terminates the program, as it
does on the first screen.
Well, you are now an expert at creating and using your own custom
gadgets from within Amiga Basic! Use them well and spruce up those
programs!